﻿using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace Nomo.UnityCoreModule
{
    /// <summary>
    /// 矩形变换扩展类。
    /// </summary>
    public static unsafe class RectTransformExtension
    {
        private static          bool      _shouldDoIntSnapping;
        private static readonly Vector3[] Corners = new Vector3[4];

        static RectTransformExtension()
        {
            UpdateShouldDoIntSnapping(UnityEngine.Object.FindObjectOfType<Canvas>());
        }

        /// <summary>
        /// <para>（注释分为多个段落，请打开注释详情界面来查看。）</para>
        /// <para>如果</para>
        /// <para>接下来要使用本类的方法来操作的矩形变换所在的画布的 <see cref="Canvas.renderMode"/> 为 <see cref="RenderMode.ScreenSpaceOverlay"/> 或 <see cref="RenderMode.ScreenSpaceCamera"/> ，而之前的为 <see cref="RenderMode.WorldSpace"/></para>
        /// <para>或者</para>
        /// <para>接下来要使用本类的方法来操作的矩形变换所在的画布的 <see cref="Canvas.renderMode"/> 为 <see cref="RenderMode.WorldSpace"/> ，而之前的为 <see cref="RenderMode.ScreenSpaceOverlay"/> 或 <see cref="RenderMode.ScreenSpaceCamera"/></para>
        /// <para>，</para>
        /// <para>那么</para>
        /// <para>请调用一次本方法，</para>
        /// <para>否则</para>
        /// <para>结果可能不符合预期。</para>
        /// </summary>
        /// <param name="canvas">接下来要使用本类的方法来操作的矩形变换所在的画布。</param>
        public static void UpdateShouldDoIntSnapping(Canvas canvas)
        {
            _shouldDoIntSnapping = canvas && canvas.renderMode != RenderMode.WorldSpace;
        }

        /// <seealso cref="UpdateShouldDoIntSnapping(UnityEngine.Canvas)"/>
        public static void UpdateShouldDoIntSnapping(RectTransform rectTransform)
        {
            UpdateShouldDoIntSnapping(rectTransform.GetComponentInParent<Canvas>());
        }

        /// <summary>
        /// 获取矩形变换的子物体。
        /// </summary>
        /// <param name="rectTransform">矩形变换。</param>
        /// <param name="list">用于存放结果的列表。</param>
        /// <exception cref="ArgumentNullException"><paramref name="rectTransform"/> 或 <paramref name="list"/> 为 <see langword="true"/> .</exception>
        public static void GetChildren(this RectTransform rectTransform, List<RectTransform> list)
        {
            if (rectTransform == null)
            {
                throw new ArgumentNullException(nameof(rectTransform));
            }
            if (list == null)
            {
                throw new ArgumentNullException(nameof(list));
            }
            list.Clear();
            var childCount = rectTransform.childCount;
            for (var i = childCount - 1; i >= 0; i--)
            {
                list.Add((RectTransform) rectTransform.GetChild(i));
            }
        }

        /// <summary>
        /// 为 <see cref="UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate"/> 提供参数。
        /// </summary>
        /// <param name="rectTransform">带有 <see cref="UnityEngine.UI.ILayoutElement"/> 组件的物体的矩形变换。</param>
        /// <returns></returns>
        /// <exception cref="System.ArgumentNullException"><paramref name="rectTransform"/> 为 <see langword="null"/> .</exception>
        /// <exception cref="System.InvalidOperationException"><paramref name="rectTransform"/> 上没有 <see cref="UnityEngine.UI.ILayoutElement"/> 组件，或 <paramref name="rectTransform"/> 没有父变换，或 <paramref name="rectTransform"/> 的父变换不是 <see cref="UnityEngine.RectTransform"/> 类型，或 <paramref name="rectTransform"/> 的父变换上没有 <see cref="UnityEngine.UI.ILayoutElement"/> 组件</exception>
        public static RectTransform GetLayoutRoot(this RectTransform rectTransform)
        {
            if (rectTransform == null)
            {
                throw new ArgumentNullException(nameof(rectTransform));
            }

            if (rectTransform.GetComponent<UnityEngine.UI.ILayoutElement>() == null)
            {
                throw new InvalidOperationException($"{rectTransform} 上没有 {nameof(UnityEngine.UI.ILayoutElement)} 组件。");
            }

            var parent = rectTransform.parent as RectTransform;

            if (parent == null)
            {
                throw new InvalidOperationException($"{rectTransform} 没有父变换，或者其父变换不是 {nameof(RectTransform)} .");
            }

            if (parent.GetComponent<UnityEngine.UI.ILayoutController>() == null)
            {
                throw new InvalidOperationException($"{rectTransform} 的父变换上没有 {nameof(UnityEngine.UI.ILayoutElement)} 组件。");
            }

            while (parent.parent is RectTransform r && r.GetComponent<UnityEngine.UI.ILayoutController>() != null)
            {
                parent = r;
            }

            return parent;
        }

        #region 矩形

        /// <summary>
        /// 返回一个值，这个值指示这个矩形是否包含另一个矩形。
        /// </summary>
        /// <param name="rect">这个矩形。</param>
        /// <param name="other">另一个矩形。</param>
        /// <returns>这个矩形包含另一个矩形吗？</returns>
        public static bool Contains(this Rect rect, Rect other)
        {
            return rect == other || rect.xMin <= other.xMin && rect.xMax >= other.xMax && rect.yMin <= other.yMin && rect.yMax >= other.yMax;
        }

        /// <summary>
        /// 返回矩形内的随机坐标。
        /// </summary>
        /// <param name="rect">这个矩形。</param>
        /// <returns>矩形内的随机坐标。</returns>
        public static Vector2 RandomInside(this Rect rect)
        {
            return new Vector2(UnityEngine.Random.Range(rect.xMin, rect.xMax), UnityEngine.Random.Range(rect.yMin, rect.yMax));
        }

        /// <summary>
        /// 返回矩形变换在世界坐标下的矩形。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <returns>矩形变换在世界坐标下的矩形（不考虑旋转）。</returns>
        public static Rect AdjustRect(this RectTransform rectTransform)
        {
            var rect = rectTransform.rect;
            var size = rect.size;
            size      *= rectTransform.lossyScale;
            rect.size =  size;
            var pivot = rectTransform.pivot;
            rect.center = (Vector2) rectTransform.position + new Vector2((0.5F - pivot.x) * size.x, (0.5F - pivot.y) * size.y);
            return rect;
        }

        /// <summary>
        /// 返回矩形变换在世界坐标下的矩形（不考虑缩放）。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <returns>矩形变换在世界坐标下的矩形（不考虑旋转和缩放）。</returns>
        public static Rect AdjustRectNonScaled(this RectTransform rectTransform)
        {
            var rect  = rectTransform.rect;
            var size  = rect.size;
            var pivot = rectTransform.pivot;
            rect.center = (Vector2) rectTransform.position + new Vector2((0.5F - pivot.x) * size.x, (0.5F - pivot.y) * size.y);
            return rect;
        }

        /// <summary>
        /// 返回矩形变换在世界坐标下的矩形的各种属性。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <returns>矩形变换在世界坐标下的矩形的各种属性。</returns>
        public static (float width, float height, float xMin, float xMax, float yMin, float yMax) AdjustRectStatus(this RectTransform rectTransform)
        {
            var rect = rectTransform.AdjustRect();
            return (rect.width, rect.height, rect.xMin, rect.xMax, rect.yMin, rect.yMax);
        }

        /// <summary>
        /// 返回矩形变换在世界坐标下的矩形（不考虑缩放）的各种属性。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <returns>矩形变换在世界坐标下的矩形（不考虑缩放）的各种属性。</returns>
        public static (float width, float height, float xMin, float xMax, float yMin, float yMax) AdjustRectNonScaledStatus(this RectTransform rectTransform)
        {
            var rect = rectTransform.AdjustRectNonScaled();
            return (rect.width, rect.height, rect.xMin, rect.xMax, rect.yMin, rect.yMax);
        }

        /// <summary>
        /// 返回矩形变换在世界坐标下的矩形内的随机一点。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <returns>矩形变换在世界坐标下的矩形内的随机一点。</returns>
        public static Vector3 RandomInside(this RectTransform rectTransform)
        {
            Vector3 point = rectTransform.AdjustRect().RandomInside();
            point.z = rectTransform.position.z;
            return point;
        }

        #endregion

        #region 取值

        /// <summary>
        /// 如果这个矩形变换的两个锚点在水平方向上重合，那么就获取这个矩形变换相对于锚点的水平偏移量。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <returns>水平偏移量。</returns>
        /// <exception cref="InvalidOperationException">当这个矩形变换不满足获取这个值的条件时，抛出此异常。</exception>
        public static float GetPosX(this RectTransform rectTransform)
        {
            if (rectTransform.anchorMin.x == rectTransform.anchorMax.x)
            {
                return rectTransform.anchoredPosition.x;
            }

            throw new InvalidOperationException("这个矩形变换不满足获取这个值的条件。");
        }

        /// <summary>
        /// 如果这个矩形变换的两个锚点在垂直方向上重合，那么就获取这个矩形变换相对于锚点的垂直偏移量。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <returns>垂直偏移量。</returns>
        /// <exception cref="InvalidOperationException">当这个矩形变换不满足获取这个值的条件时，抛出此异常。</exception>
        public static float GetPosY(this RectTransform rectTransform)
        {
            if (rectTransform.anchorMin.y == rectTransform.anchorMax.y)
            {
                return rectTransform.anchoredPosition.y;
            }

            throw new InvalidOperationException("这个矩形变换不满足获取这个值的条件。");
        }

        /// <summary>
        /// 获取这个矩形变换的局部坐标的 z 分量。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <returns>局部坐标的 z 分量。</returns>
        public static float GetPosZ(this RectTransform rectTransform)
        {
            return rectTransform.localPosition.z;
        }

        /// <summary>
        /// 如果这个矩形变换的两个锚点在水平方向上分开，那么就获取这个矩形变换的左边到锚框（两个锚点形成的矩形）的左边的距离。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <returns>左边到锚框左边的距离。</returns>
        /// <exception cref="InvalidOperationException">当这个矩形变换不满足获取这个值的条件时，抛出此异常。</exception>
        public static float GetLeft(this RectTransform rectTransform)
        {
            if (rectTransform.anchorMin.x != rectTransform.anchorMax.x)
            {
                return rectTransform.offsetMin.x;
            }

            throw new InvalidOperationException("这个矩形变换不满足获取这个值的条件。");
        }

        /// <summary>
        /// 如果这个矩形变换的两个锚点在水平方向上分开，那么就获取这个矩形变换的右边到锚框（两个锚点形成的矩形）的右边的距离。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <returns>右边到锚框右边的距离。</returns>
        /// <exception cref="InvalidOperationException">当这个矩形变换不满足获取这个值的条件时，抛出此异常。</exception>
        public static float GetRight(this RectTransform rectTransform)
        {
            if (rectTransform.anchorMin.x != rectTransform.anchorMax.x)
            {
                return -rectTransform.offsetMax.x;
            }

            throw new InvalidOperationException("这个矩形变换不满足获取这个值的条件。");
        }

        /// <summary>
        /// 如果这个矩形变换的两个锚点在垂直方向上分开，那么就获取这个矩形变换的底边到锚框（两个锚点形成的矩形）的底边的距离。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <returns>底边到锚框底边的距离。</returns>
        /// <exception cref="InvalidOperationException">当这个矩形变换不满足获取这个值的条件时，抛出此异常。</exception>
        public static float GetBottom(this RectTransform rectTransform)
        {
            if (rectTransform.anchorMin.y != rectTransform.anchorMax.y)
            {
                return rectTransform.offsetMin.y;
            }

            throw new InvalidOperationException("这个矩形变换不满足获取这个值的条件。");
        }

        /// <summary>
        /// 如果这个矩形变换的两个锚点在垂直方向上分开，那么就获取这个矩形变换的顶边到锚框（两个锚点形成的矩形）的顶边的距离。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <returns>顶边到锚框顶边的距离。</returns>
        /// <exception cref="InvalidOperationException">当这个矩形变换不满足获取这个值的条件时，抛出此异常。</exception>
        public static float GetTop(this RectTransform rectTransform)
        {
            if (rectTransform.anchorMin.y != rectTransform.anchorMax.y)
            {
                return -rectTransform.offsetMax.y;
            }

            throw new InvalidOperationException("这个矩形变换不满足获取这个值的条件。");
        }

        /// <summary>
        /// 如果这个矩形变换的两个锚点在水平方向上重合，那么就获取这个矩形变换的宽度。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <returns>宽度。</returns>
        /// <exception cref="InvalidOperationException">当这个矩形变换不满足获取这个值的条件时，抛出此异常。</exception>
        public static float GetWidth(this RectTransform rectTransform)
        {
            if (rectTransform.anchorMin.x == rectTransform.anchorMax.x)
            {
                return rectTransform.sizeDelta.x;
            }

            throw new InvalidOperationException("这个矩形变换不满足获取这个值的条件。");
        }

        /// <summary>
        /// 如果这个矩形变换的两个锚点在垂直方向上重合，那么就获取这个矩形变换的高度。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <returns>高度。</returns>
        /// <exception cref="InvalidOperationException">当这个矩形变换不满足获取这个值的条件时，抛出此异常。</exception>
        public static float GetHeight(this RectTransform rectTransform)
        {
            if (rectTransform.anchorMin.x == rectTransform.anchorMax.x)
            {
                return rectTransform.sizeDelta.y;
            }

            throw new InvalidOperationException("这个矩形变换不满足获取这个值的条件。");
        }

        /// <summary>
        /// 获取这个矩形变换在视觉上的宽度。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <returns>宽度。</returns>
        public static float GetLogicalWidth(this RectTransform rectTransform)
        {
            return rectTransform.rect.width;
        }

        /// <summary>
        /// 获取这个矩形变换在视觉上的高度。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <returns>高度。</returns>
        public static float GetLogicalHeight(this RectTransform rectTransform)
        {
            return rectTransform.rect.height;
        }

        #endregion

        #region 赋值

        /// <summary>
        /// 使这个矩形变换与锚框对其。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <returns>这个矩形变换。</returns>
        public static RectTransform AlignToAnchors(this RectTransform rectTransform)
        {
            rectTransform.offsetMin = Vector2.zero;
            rectTransform.offsetMax = Vector2.zero;

            return rectTransform;
        }

        /// <summary>
        /// 设置这个矩形变换的枢轴的值。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="x">枢轴的 x 分量。如果不需要重新设置，请传入 <see langword="null"/> .</param>
        /// <param name="y">枢轴的 y 分量。如果不需要重新设置，请传入 <see langword="null"/> .</param>
        /// <returns>这个矩形变换。</returns>
        public static RectTransform SetPivot(this RectTransform rectTransform, float? x, float? y)
        {
            return rectTransform.SetPivot(x, y, true);
        }

        /// <summary>
        /// 设置这个矩形变换的枢轴的值。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="x">枢轴的 x 分量。如果不需要重新设置，请传入 <see langword="null"/> .</param>
        /// <param name="y">枢轴的 y 分量。如果不需要重新设置，请传入 <see langword="null"/> .</param>
        /// <param name="smart">如果为 <see langword="true"/> ，那么这个矩形变换会“保持原样”；否则，直接对 <see cref="RectTransform.pivot"/> 赋值。</param>
        /// <returns>这个矩形变换。</returns>
        public static RectTransform SetPivot(this RectTransform rectTransform, float? x, float? y, bool smart)
        {
            if (!x.HasValue && !y.HasValue)
            {
                return rectTransform;
            }

            return smart ? SetPivotSmart(rectTransform, x, y) : SetPivotRaw(rectTransform, x, y);
        }

        /// <summary>
        /// 设置这个矩形变换的左下锚点的值。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="x">左下锚点的 x 分量。如果不需要重新设置，请传入 <see langword="null"/> .</param>
        /// <param name="y">左下锚点的 y 分量。如果不需要重新设置，请传入 <see langword="null"/> .</param>
        /// <returns>这个矩形变换。</returns>
        public static RectTransform SetAnchorMin(this RectTransform rectTransform, float? x, float? y)
        {
            return rectTransform.SetAnchorMin(x, y, true);
        }

        /// <summary>
        /// 设置这个矩形变换的左下锚点的值。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="x">左下锚点的 x 分量。如果不需要重新设置，请传入 <see langword="null"/> .</param>
        /// <param name="y">左下锚点的 y 分量。如果不需要重新设置，请传入 <see langword="null"/> .</param>
        /// <param name="smart">如果为 <see langword="true"/> ，那么这个矩形变换会“保持原样”；否则，直接对 <see cref="RectTransform.anchorMin"/> 赋值。</param>
        /// <returns>这个矩形变换。</returns>
        public static RectTransform SetAnchorMin(this RectTransform rectTransform, float? x, float? y, bool smart)
        {
            if (!x.HasValue && !y.HasValue)
            {
                return rectTransform;
            }

            return smart ? SetAnchorMinSmart(rectTransform, x, y) : SetAnchorMinRaw(rectTransform, x, y);
        }

        /// <summary>
        /// 设置这个矩形变换的右上锚点的值。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="x">右上锚点的 x 分量。如果不需要重新设置，请传入 <see langword="null"/> .</param>
        /// <param name="y">右上锚点的 y 分量。如果不需要重新设置，请传入 <see langword="null"/> .</param>
        /// <returns>这个矩形变换。</returns>
        public static RectTransform SetAnchorMax(this RectTransform rectTransform, float? x, float? y)
        {
            return rectTransform.SetAnchorMax(x, y, true);
        }

        /// <summary>
        /// 设置这个矩形变换的右上锚点的值。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="x">右上锚点的 x 分量。如果不需要重新设置，请传入 <see langword="null"/> .</param>
        /// <param name="y">右上锚点的 y 分量。如果不需要重新设置，请传入 <see langword="null"/> .</param>
        /// <param name="asIs">如果为 <see langword="true"/> ，那么这个矩形变换会“保持原样”；否则，直接对 <see cref="RectTransform.anchorMax"/> 赋值。</param>
        /// <returns>这个矩形变换。</returns>
        public static RectTransform SetAnchorMax(this RectTransform rectTransform, float? x, float? y, bool asIs)
        {
            if (!x.HasValue && !y.HasValue)
            {
                return rectTransform;
            }

            return asIs ? SetAnchorMaxSmart(rectTransform, x, y) : SetAnchorMaxRaw(rectTransform, x, y);
        }

        /// <summary>
        /// 如果这个矩形变换的两个锚点在水平方向上重合，那么就设置这个矩形变换相对于锚点的水平偏移量。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="posX">水平偏移量。</param>
        /// <returns>这个矩形变换。</returns>
        public static RectTransform SetPosX(this RectTransform rectTransform, float posX)
        {
            if (rectTransform.anchorMin.x == rectTransform.anchorMax.x)
            {
                var anchoredPosition = rectTransform.anchoredPosition;
                anchoredPosition.x             = posX;
                rectTransform.anchoredPosition = anchoredPosition;
            }
            return rectTransform;
        }

        /// <summary>
        /// 如果这个矩形变换的两个锚点在垂直方向上重合，那么就设置这个矩形变换相对于锚点的垂直偏移量。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="posY">垂直偏移量。</param>
        /// <returns>这个矩形变换。</returns>
        public static RectTransform SetPosY(this RectTransform rectTransform, float posY)
        {
            if (rectTransform.anchorMin.y == rectTransform.anchorMax.y)
            {
                var anchoredPosition = rectTransform.anchoredPosition;
                anchoredPosition.y             = posY;
                rectTransform.anchoredPosition = anchoredPosition;
            }
            return rectTransform;
        }

        /// <summary>
        /// 设置这个矩形变换的局部坐标的 z 分量。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="posZ">局部坐标的 z 分量。</param>
        /// <returns>这个矩形变换。</returns>
        public static RectTransform SetPosZ(this RectTransform rectTransform, float posZ)
        {
            var localPosition = rectTransform.localPosition;
            localPosition.z             = posZ;
            rectTransform.localPosition = localPosition;
            return rectTransform;
        }

        /// <summary>
        /// 如果这个矩形变换的两个锚点在水平方向上分开，那么就设置这个矩形变换的左边到锚框（两个锚点形成的矩形）的左边的距离。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="left">左边到锚框左边的距离。</param>
        /// <returns>这个矩形变换。</returns>
        public static RectTransform SetLeft(this RectTransform rectTransform, float left)
        {
            if (rectTransform.anchorMin.x != rectTransform.anchorMax.x)
            {
                var offsetMin = rectTransform.offsetMin;
                offsetMin.x             = left;
                rectTransform.offsetMin = offsetMin;
            }
            return rectTransform;
        }

        /// <summary>
        /// 如果这个矩形变换的两个锚点在水平方向上分开，那么就设置这个矩形变换的右边到锚框（两个锚点形成的矩形）的右边的距离。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="right">右边到锚框右边的距离。</param>
        /// <returns>这个矩形变换。</returns>
        public static RectTransform SetRight(this RectTransform rectTransform, float right)
        {
            if (rectTransform.anchorMin.x != rectTransform.anchorMax.x)
            {
                var offsetMax = rectTransform.offsetMax;
                offsetMax.x             = -right;
                rectTransform.offsetMax = offsetMax;
            }
            return rectTransform;
        }

        /// <summary>
        /// 如果这个矩形变换的两个锚点在垂直方向上分开，那么就设置这个矩形变换的底边到锚框（两个锚点形成的矩形）的底边的距离。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="bottom">底边到锚框底边的距离。</param>
        /// <returns>这个矩形变换。</returns>
        public static RectTransform SetBottom(this RectTransform rectTransform, float bottom)
        {
            if (rectTransform.anchorMin.y != rectTransform.anchorMax.y)
            {
                var offsetMin = rectTransform.offsetMin;
                offsetMin.y             = bottom;
                rectTransform.offsetMin = offsetMin;
            }
            return rectTransform;
        }

        /// <summary>
        /// 如果这个矩形变换的两个锚点在垂直方向上分开，那么就设置这个矩形变换的顶边到锚框（两个锚点形成的矩形）的顶边的距离。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="top">顶边到锚框顶边的距离。</param>
        /// <returns>这个矩形变换。</returns>
        public static RectTransform SetTop(this RectTransform rectTransform, float top)
        {
            if (rectTransform.anchorMin.y != rectTransform.anchorMax.y)
            {
                var offsetMax = rectTransform.offsetMax;
                offsetMax.y             = -top;
                rectTransform.offsetMax = offsetMax;
            }
            return rectTransform;
        }

        /// <summary>
        /// 如果这个矩形变换的两个锚点在水平方向上重合，那么就设置这个矩形变换的宽度。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="width">宽度。</param>
        /// <returns>这个矩形变换。</returns>
        public static RectTransform SetWidth(this RectTransform rectTransform, float width)
        {
            if (rectTransform.anchorMin.x == rectTransform.anchorMax.x)
            {
                var sizeDelta = rectTransform.sizeDelta;
                sizeDelta.x             = width;
                rectTransform.sizeDelta = sizeDelta;
            }
            return rectTransform;
        }

        /// <summary>
        /// 如果这个矩形变换的两个锚点在垂直方向上重合，那么就设置这个矩形变换的高度。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="height">高度。</param>
        /// <returns>这个矩形变换。</returns>
        public static RectTransform SetHeight(this RectTransform rectTransform, float height)
        {
            if (rectTransform.anchorMin.y == rectTransform.anchorMax.y)
            {
                var sizeDelta = rectTransform.sizeDelta;
                sizeDelta.y             = height;
                rectTransform.sizeDelta = sizeDelta;
            }
            return rectTransform;
        }

        /// <summary>
        /// 设置这个矩形变换在视觉上的宽度。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="width">宽度。</param>
        /// <returns>这个矩形变换。</returns>
        public static RectTransform SetLogicalWidth(this RectTransform rectTransform, float width)
        {
            var sizeDelta     = rectTransform.sizeDelta;
            var parentWidth   = (double) ((RectTransform) rectTransform.parent).rect.width;
            var anchorMinX    = (double) rectTransform.anchorMin.x;
            var anchorMaxX    = (double) rectTransform.anchorMax.x;
            var anchorXDelta  = anchorMaxX - anchorMinX;
            var newSizeDeltaX = width      - parentWidth * anchorXDelta;
            sizeDelta.x             = (float) newSizeDeltaX;
            rectTransform.sizeDelta = sizeDelta;
            return rectTransform;
        }

        /// <summary>
        /// 设置这个矩形变换在视觉上的高度。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="height">高度。</param>
        /// <returns>这个矩形变换。</returns>
        public static RectTransform SetLogicalHeight(this RectTransform rectTransform, float height)
        {
            var sizeDelta     = rectTransform.sizeDelta;
            var parentHeight  = (double) ((RectTransform) rectTransform.parent).rect.height;
            var anchorMinY    = (double) rectTransform.anchorMin.y;
            var anchorMaxY    = (double) rectTransform.anchorMax.y;
            var anchorYDelta  = anchorMaxY - anchorMinY;
            var newSizeDeltaY = height     - parentHeight * anchorYDelta;
            sizeDelta.y             = (float) newSizeDeltaY;
            rectTransform.sizeDelta = sizeDelta;
            return rectTransform;
        }

        #endregion

        #region 判断

        #region 合法

        /// <summary>
        /// <para>检查这个矩形变换是否合法。</para>
        /// <para>合法，指的是这个矩形变换具有父变换，并且父变换也是一个矩形变换。</para>
        /// <para>画布和画布以外的矩形变换无法通过这个检测。</para>
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <returns>这个矩形变换。</returns>
        /// <exception cref="NullReferenceException">当这个矩形变换的引用为空时，抛出此异常。</exception>
        /// <exception cref="InvalidOperationException">当这个矩形变换没有父变换，或父变换不是一个矩形变换时，抛出此异常。</exception>
        public static RectTransform Validate(this RectTransform rectTransform)
        {
            if (!rectTransform)
            {
                throw new NullReferenceException("这个矩形变换的引用为空！");
            }

            var parent = rectTransform.parent;
            if (!parent)
            {
                throw new InvalidOperationException("这个矩形变换没有父变换！");
            }

            if (!(parent is RectTransform))
            {
                throw new InvalidOperationException("这个矩形变换的父变换不是一个矩形变换！");
            }

            return rectTransform;
        }

        #endregion

        #region 包含

        /// <summary>
        /// 返回一个值，这个值指示这个矩形变换是否包含另一个矩形变换。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="other">另一个矩形变换。</param>
        /// <returns>这个矩形变换包含另一个矩形变换吗？</returns>
        public static bool Contains(this RectTransform rectTransform, RectTransform other)
        {
            if (rectTransform == other)
            {
                return true;
            }

            other.GetWorldCorners(Corners);
            return Corners.All(rectTransform.Contains);
        }

        /// <summary>
        /// 返回一个值，这个值指示这个矩形变换是否包含一个点。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="point">点（世界坐标）。</param>
        /// <returns>这个矩形变换包含这个点吗？</returns>
        public static bool Contains(this RectTransform rectTransform, Vector2 point)
        {
            return rectTransform.rect.Contains((Vector2) rectTransform.InverseTransformPoint(point));
        }

        /// <summary>
        /// 返回一个值，这个值指示这个矩形变换是否包含一个点。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="point">点（世界坐标）。</param>
        /// <returns>这个矩形变换包含这个点吗？</returns>
        public static bool Contains(this RectTransform rectTransform, Vector3 point)
        {
            return rectTransform.Contains((Vector2) point);
        }

        /// <summary>
        /// 返回一个值，这个值指示这个矩形变换是否在一个矩形变换内部。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="other">另一个矩形变换。</param>
        /// <returns>这个矩形变换在一个矩形变换内部吗？</returns>
        public static bool Inside(this RectTransform rectTransform, RectTransform other)
        {
            return other.Contains(rectTransform);
        }

        /// <summary>
        /// 返回一个值，这个值指示这个点是否在一个矩形变换内部。
        /// </summary>
        /// <param name="point">这个点（世界坐标）。</param>
        /// <param name="rectTransform">矩形变换。</param>
        /// <returns>这个点在矩形变换里吗？</returns>
        public static bool Inside(this Vector2 point, RectTransform rectTransform)
        {
            return rectTransform.Contains(point);
        }

        /// <summary>
        /// 返回一个值，这个值指示这个点是否在一个矩形变换内部。
        /// </summary>
        /// <param name="point">这个点（世界坐标）。</param>
        /// <param name="rectTransform">矩形变换。</param>
        /// <returns>这个点在矩形变换里吗？</returns>
        public static bool Inside(this Vector3 point, RectTransform rectTransform)
        {
            return rectTransform.Contains(point);
        }

        #endregion

        #region 重叠

        /// <summary>
        /// 返回一个值，这个值指示这个矩形变换是否和另一个矩形变换有重叠部分。
        /// </summary>
        /// <param name="rectTransform">这个矩形变换。</param>
        /// <param name="other">另一个矩形变换。</param>
        /// <returns>这个矩形变换和另一个矩形变换有重叠部分吗？</returns>
        public static bool OverLaps(this RectTransform rectTransform, RectTransform other)
        {
            if (rectTransform == other)
            {
                return true;
            }
            rectTransform.GetWorldCorners(Corners);
            var rectTransformCorners = stackalloc Vector2[4]; // rectTransform 的 4 个顶点的世界坐标
            for (var i = 0; i != 4; i++)
            {
                rectTransformCorners[i] = Corners[i];
            }
            other.GetWorldCorners(Corners);
            var otherCorners = stackalloc Vector2[4]; // other 的 4 个顶点的世界坐标
            for (var i = 0; i != 4; i++)
            {
                otherCorners[i] = Corners[i];
            }
            // 判断 rectTransform 是否有某一条边与 other 的某一条边相交
            for (var i = 0; i < 4; i++)
            for (var j = 0; j < 4; j++)
            {
                // "n & 3" 等价于 "n % 4"
                if (NomoMath.LineSegmentIntersection(rectTransformCorners[i & 3], rectTransformCorners[(i + 1) & 3], otherCorners[j & 3], otherCorners[(j + 1) & 3], out _))
                {
                    return true;
                }
            }
            // 判断 rectTransform 是否完全内含于 other
            for (var i = 0; i < 4; i++)
            {
                if (other.Contains(rectTransformCorners[i]))
                {
                    return true;
                }
            }
            // 判断 other 是否完全内含于 rectTransform
            for (var i = 0; i < 4; i++)
            {
                if (rectTransform.Contains(otherCorners[i]))
                {
                    return true;
                }
            }
            return false;
        }

        #endregion

        #endregion

        #region 变换

        /// <seealso cref="TransformExtension.DestroyChildren"/>
        public static RectTransform DestroyChildren(this RectTransform rectTransform)
        {
            return (RectTransform) ((Transform) rectTransform).DestroyChildren();
        }

        /// <seealso cref="TransformExtension.DestroyChildrenImmediately"/>
        public static RectTransform DestroyChildrenImmediately(this RectTransform rectTransform)
        {
            return (RectTransform) ((Transform) rectTransform).DestroyChildrenImmediately();
        }

        /// <seealso cref="TransformExtension.SetPosition(UnityEngine.Transform,System.Nullable{float},System.Nullable{float},System.Nullable{float})"/>
        public static RectTransform SetPosition(this RectTransform rectTransform, float? x, float? y, float? z)
        {
            return (RectTransform) ((Transform) rectTransform).SetPosition(x, y, z);
        }

        /// <seealso cref="TransformExtension.SetPosition(UnityEngine.Transform,System.Nullable{float},System.Nullable{float})"/>
        public static RectTransform SetPosition(this RectTransform rectTransform, float? x, float? y)
        {
            return (RectTransform) ((Transform) rectTransform).SetPosition(x, y);
        }

        /// <seealso cref="TransformExtension.SetLocalPosition(UnityEngine.Transform,System.Nullable{float},System.Nullable{float},System.Nullable{float})"/>
        public static RectTransform SetLocalPosition(this RectTransform rectTransform, float? x, float? y, float? z)
        {
            return (RectTransform) ((Transform) rectTransform).SetLocalPosition(x, y, z);
        }

        /// <seealso cref="TransformExtension.SetLocalPosition(UnityEngine.Transform,System.Nullable{float},System.Nullable{float})"/>
        public static RectTransform SetLocalPosition(this RectTransform rectTransform, float? x, float? y)
        {
            return (RectTransform) ((Transform) rectTransform).SetLocalPosition(x, y);
        }

        /// <seealso cref="TransformExtension.SetLocalScale(UnityEngine.Transform,System.Nullable{float},System.Nullable{float},System.Nullable{float})"/>
        public static RectTransform SetLocalScale(this RectTransform rectTransform, float? x, float? y, float? z)
        {
            return (RectTransform) ((Transform) rectTransform).SetLocalScale(x, y, z);
        }

        /// <seealso cref="TransformExtension.SetLocalScale(UnityEngine.Transform,System.Nullable{float},System.Nullable{float})"/>
        public static RectTransform SetLocalScale(this RectTransform rectTransform, float? x, float? y)
        {
            return (RectTransform) ((Transform) rectTransform).SetLocalScale(x, y);
        }

        #endregion

        #region 内部实现

        private static RectTransform SetPivotSmart(RectTransform rectTransform, float? x, float? y)
        {
            var pivot              = rectTransform.pivot;
            var referenceCornerOld = GetRectReferenceCorner(rectTransform);
            if (x.HasValue)
            {
                pivot.x = x.Value;
            }
            if (y.HasValue)
            {
                pivot.y = y.Value;
            }
            rectTransform.pivot = pivot;
            var referenceCornerNew    = GetRectReferenceCorner(rectTransform);
            var referenceCornerOffset = referenceCornerNew - referenceCornerOld;
            rectTransform.anchoredPosition -= (Vector2) referenceCornerOffset;
            var position = rectTransform.position;
            position.z             -= referenceCornerOffset.z;
            rectTransform.position =  position;
            return rectTransform;
        }

        private static RectTransform SetPivotRaw(RectTransform rectTransform, float? x, float? y)
        {
            var pivot = rectTransform.pivot;
            if (x.HasValue)
            {
                pivot.x = x.Value;
            }
            if (y.HasValue)
            {
                pivot.y = y.Value;
            }
            rectTransform.pivot = pivot;
            return rectTransform;
        }

        private static RectTransform SetAnchorMinSmart(RectTransform rectTransform, float? x, float? y)
        {
            var anchorMin        = rectTransform.anchorMin;
            var anchoredPosition = rectTransform.anchoredPosition;
            var sizeDelta        = rectTransform.sizeDelta;
            var parent           = (RectTransform) rectTransform.parent;
            if (x.HasValue)
            {
                var sizeDeltaDeltaX        = _shouldDoIntSnapping ? Mathf.Round((float) (((double) x.Value - anchorMin.x) * parent.rect.size.x)) : 0.0F;
                var anchoredPositionDeltaX = (float) (sizeDeltaDeltaX * (1D - rectTransform.pivot.x));
                anchorMin.x        =  x.Value;
                anchoredPosition.x -= anchoredPositionDeltaX;
                sizeDelta.x        += sizeDeltaDeltaX;
            }
            if (y.HasValue)
            {
                var sizeDeltaDeltaY        = _shouldDoIntSnapping ? Mathf.Round((float) (((double) y.Value - anchorMin.y) * parent.rect.size.y)) : 0.0F;
                var anchoredPositionDeltaY = (float) (sizeDeltaDeltaY * (1D - rectTransform.pivot.y));
                anchorMin.y        =  y.Value;
                anchoredPosition.y -= anchoredPositionDeltaY;
                sizeDelta.y        += sizeDeltaDeltaY;
            }
            rectTransform.anchorMin        = anchorMin;
            rectTransform.anchoredPosition = anchoredPosition;
            rectTransform.sizeDelta        = sizeDelta;
            return rectTransform;
        }

        private static RectTransform SetAnchorMinRaw(RectTransform rectTransform, float? x, float? y)
        {
            var anchorMin = rectTransform.anchorMin;
            if (x.HasValue)
            {
                anchorMin.x = x.Value;
            }
            if (y.HasValue)
            {
                anchorMin.y = y.Value;
            }
            rectTransform.anchorMin = anchorMin;
            return rectTransform;
        }

        private static RectTransform SetAnchorMaxSmart(RectTransform rectTransform, float? x, float? y)
        {
            var anchorMax        = rectTransform.anchorMax;
            var anchoredPosition = rectTransform.anchoredPosition;
            var sizeDelta        = rectTransform.sizeDelta;
            var parent           = (RectTransform) rectTransform.parent;
            if (x.HasValue)
            {
                var sizeDeltaDeltaX        = _shouldDoIntSnapping ? Mathf.Round((float) (((double) x.Value - anchorMax.x) * parent.rect.size.x)) : 0.0F;
                var anchoredPositionDeltaX = (float) ((double) sizeDeltaDeltaX * rectTransform.pivot.x);
                anchorMax.x        =  x.Value;
                anchoredPosition.x -= anchoredPositionDeltaX;
                sizeDelta.x        -= sizeDeltaDeltaX;
            }
            if (y.HasValue)
            {
                var sizeDeltaDeltaY        = _shouldDoIntSnapping ? Mathf.Round((float) (((double) y.Value - anchorMax.y) * parent.rect.size.y)) : 0.0F;
                var anchoredPositionDeltaY = (float) ((double) sizeDeltaDeltaY * rectTransform.pivot.y);
                anchorMax.y        =  y.Value;
                anchoredPosition.y -= anchoredPositionDeltaY;
                sizeDelta.y        -= sizeDeltaDeltaY;
            }
            rectTransform.anchorMax        = anchorMax;
            rectTransform.anchoredPosition = anchoredPosition;
            rectTransform.sizeDelta        = sizeDelta;
            return rectTransform;
        }

        private static RectTransform SetAnchorMaxRaw(RectTransform rectTransform, float? x, float? y)
        {
            var anchorMax = rectTransform.anchorMax;
            if (x.HasValue)
            {
                anchorMax.x = x.Value;
            }
            if (y.HasValue)
            {
                anchorMax.y = y.Value;
            }
            rectTransform.anchorMax = anchorMax;
            return rectTransform;
        }

        private static Vector3 GetRectReferenceCorner(RectTransform rectTransform)
        {
            rectTransform.GetWorldCorners(Corners);
            return rectTransform.parent.InverseTransformPoint(Corners[0]);
        }

        #endregion
    }
}
